• Attaching and Detaching Embedded Frames - ODFDraw, ODFContainer
• Hide Show Embedded Frames - ODFEmbed
• Restricting Embedding - ODFTable
• Changing the Used Shape (Embedded Frame) - ODFBitmap, ODFClock
• Used Shape Changed (Containing Frame) - All Embedding Parts
Embedding Content Model
Implementing embedding requires you to first have a content model and then understand how to mix in the ODF's FW_MProxy object into your content model classes.
ODFEmbed: the content model is very simple: a pointer to a CEmbedProxy (subclass of FW_MProxy). This pointer is NULL if there is no embedded frame. Because the size and position of the embedded frame can be calculated at any time using the size of the display frame (CEmbedFrame) there is no reason to cache those values inside CEmbedProxy.
ODFContainer: ODFContainer is a step above ODFEmbed. Instead of being able to embed just one frame, ODFContainer allows you to embed an unlimited number of frames. CContainerProxy is a subclass of FW_MProxy and extend it by keeping track of the size and position of embedded frames (/facets) in content coordinates. CContainerProxy objects are simply kept in an ordered collection matching the z-ordering of the embedded facets.
ODFTable: although ODFTable is very similar to ODFContainer (allows multiple embedded frames), ODFTable has a very different content model. CTableProxy objects are not kept in an ordered collection but instead ODFTable maintains an array of cells (CCell). Each CCell object has a pointer to a CTableProxy. A NULL pointer simply means the cell is empty.
ODFDraw: ODFDraw is like ODFContainer: multiple frames can be embedded. The difference is that ODFDraw also has intrinsic content. ODFDraw keeps an ordered collection of shapes (CBaseShape) instead of just proxies. Subclasses of CBaseShape are for example: CLineShape, CRectShape.... One particular subclass of CBaseShape is CProxyShape which is in fact a mix in of CRectShape and FW_MProxy. Because it is a shape, CProxyShape has the same API as any other shape.
Resizing and Moving Embedded Frames
ODFDraw: the resizing of an embedded frame is done in CProxyShape::MapShape which is called by CResizeShapeCommand::DoIt. CProxyShape::MapShape first calls the inherited method (CProxyShape is a CRectShape) and then calls FW_MProxy::ChangeExternalTransform to reposition the embedded frame and FW_MProxy::ChangeFrameShape to change the embedded frame's frame shape. Don't forget that dragging the top-left corner handle of a shape will both change the position and size of the shape.
Dragging Embedded Frames in the Same Part
ODFDraw: look at CDrawDropCommand::DoDroppedInSameFrame. DoDroppedInSameFrame simply calls CDrawSelection::OffsetSelection.
ODFContainer: look also at CDropCommand::DoDroppedInSameFrame.
Data Interchange of a Single Embedded Frame
In ODF, the Single Embedded Frame case is handled by FW_CEmbeddingContent::SingleEmbeddedFrameInternalized. For Drag&Drop, Clipboard and Insert the content object associated with the selection is used. For Linking the content object associated with the destination link is used.
ODFDraw: look at CDrawSelectionContent::SingleEmbeddFrameInternalized, and CDrawLinkContent::SingleEmbeddFrameInternalized. Notice how both method call CDrawContent::AddSingleEmbeddedFrame. CDrawContent is the base class for both CDrawSelectionContent and CDrawLinkContent.
ODFEmbed: A menu item allows you to display the embedded frame using one and four facets. Look at CEmbedPart::DoMenu which calls CEmbedPart::ChangeEmbeddedFacets.
RequestFrameShape (Embedded Frame)
ODFBitmap: Look at CBitmapFrame::AdjustFrameSize. AdjustFrameSize is called by CBitmapFrame::DoMenu when the user changes the scaling factor and by CBitmapSelectionContent::Internalize (through CBitmapPart::AdjustFramesSize) when the bitmap is changed. Note that RequestFrameShape is not called when the frame is the root frame.
ODFClock: RequestFrameShape is called by CClockFrame::ChangeClockType.
RequestFrameShape (Containing Frame)
When an embedded frame calls RequestFrameShape, the container’s FW_MProxy::FrameShapeRequested method is called. The default implementation always grants the requested frame shape (Look at FW_MProxy::FrameShapeRequested).
ODFDraw, ODFContainer: although ODFDraw and ODFContainer don’t put any restriction on the embedded frame shape they are still overriding FrameShapeRequested to keep track of the new size of the embedded frame.
ODFTable: look at CTableProxy::FrameShapeRequested. Whatever the requested frame shape is, CTableProxy::FrameShapeRequested always returns a frame shape equal to the size of the cell.
ODFEmbed: same as ODFTable, the frame shape is always equal to the frame shape of the display minus a 15 pixels inset.
Insert
Embedding Part Editors supporting Insert must subclass FW_CInsertCommand and must override FW_CEmbeddingFrame::NewInsertCommand. The insert command must always be undoable.
All embedding ODF parts support Insert.
Drawing Embedded Facets
To keep a better control over rendering, both ODFDraw and ODFContainer draw their embedded facets instead of letting OpenDoc do it.
ODFDraw: look at CProxyShape::RenderShape.
ODFContainer: look at CProxy::Render.
Notice in both case the usage of FW_CSaveRestoreContext. Call to ODFacet::Draw and ODFacet::DrawChildren leave the grafport in a unknown state. FW_CSaveAndRestoreContext makes sure that the grafport in set back to its original state.
Attaching and Detaching Embedded Frames
FW_MProxy has two methods called DetachEmbeddedFrames and AttachEmbeddedFrames. This API should not be used to hide and show embedded frames (there is a method called HideShow for this purpose). Detach and Attach embedded frames has to do with operations which add and remove embedded frames. The following table shows the API called for each type of operations (Embed refers to FW_CPresentation::Embed)
Do Undo Redo
Clear Detach Attach Detach
Cut Detach Attach Detach
Paste Embed Detach Attach
Insert Embed Detach Attach
Drop Embed Detach Attach
Drag(Move) Detach Attach Detach
ODFDraw: DetachEmbeddedFrames is called by CProxyShape::Removed which is called every time an embedded frames is removed from the content. AttachEmbeddedFrames is called by CProxyShape::RestoreShape which is called when operations like Cut and Clear are undone.
ODFContainer: DetachEmbeddedFrames is called by CContainerPart::DetachProxy. AttachEmbeddedFrames is called by CContainerPart::AttachProxy.
Hide Show Embedded Frames
The easiest way to hide an embedded frame is to delete its facets. This is what FW_MProxy::HideShow does.
ODFEmbed: look at CEmbedPart::ChangeEmbeddedFacets. ODFEmbed uses FW_MProxy::HideShow to toggle between one and four embedded facets.
Restricting Embedding
ODFTable: in part editors like ODFDraw a frame can be embedded anywhere in the content area, there are no restrictions. In ODFTable we decided to restrict embedding to empty cells: the user cannot embed a new frame in a cell already containing an embedded frame. For operations like Insert or Paste it’s easy to test if the current selected cell has a proxy or not. During a Drag&Drop operation it is a little more complex. Two changes had to be made:
CTableFrame::NewDropCommand: the drop command is not created if the drop cell already contains an embedded frame.
CTableDropTracker: the goal here is to not highlight a destination cell if it already contains an embedded frame. This is done in BeginTracking and ContinueTracking by calling CTableDropTracker::ShouldHilite which tests first the mouse is inside a cell and then tests that the cell doesn’t already contain an embedded frame.
Changing the Used Shape (Embedded Frame)
ODFClock: when seen as analog the ODFClock uses a round used shape. Check CClockFrame::AdjustUsedShape where the calculation of the shape is done. AdjustUsedShape is called by ODF when your frame is viewed as a frame (instead of as icon or as thumbnail). To force your frame to change its used shape call UpdateUsedAndActiveShapes().
ODFBitmap: ODFBitmap changes its used shape so the bitmap is always centered inside the frame shape. See CBitmapFrame::AdjustUsedShape.
Used Shape Changed (Containing Frame)
When an embedded frame changes its used shape, your proxy object is notified (FW_MProxy::UsedShapeChanged is called). The minimum action you need to take in your override is to recalculate the clip shape of the embedded facets.